/*
 * @ : Copyright (c) 2021 Phytium Information Technology, Inc.
 *
 * SPDX-License-Identifier: Apache-2.0.
 *
 * @Date: 2021-04-29 10:21:53
 * @LastEditTime: 2021-08-16 15:28:20
 * @Description:  Description of file
 * @Modify History:
 * * * Ver   Who        Date         Changes
 * * ----- ------     --------    --------------------------------------
 */

#include "ft_can.h"
#include "ft_can_hw.h"
#include "ft_assert.h"
#include "ft_debug.h"
#include "string.h"
#include "swap.h"

#define FT_CAN_DEBUG_TAG "FT_CAN"

#define FT_CAN_DEBUG_I(format, ...) FT_DEBUG_PRINT_I(FT_CAN_DEBUG_TAG, format, ##__VA_ARGS__)
#define FT_CAN_DEBUG_E(format, ...) FT_DEBUG_PRINT_E(FT_CAN_DEBUG_TAG, format, ##__VA_ARGS__)
#define FT_CAN_DEBUG_W(format, ...) FT_DEBUG_PRINT_W(FT_CAN_DEBUG_TAG, format, ##__VA_ARGS__)
#define FT_CAN_DEBUG_E(format, ...) FT_DEBUG_PRINT_E(FT_CAN_DEBUG_TAG, format, ##__VA_ARGS__)

ft_error_t FCanCfgInitialize(FCan *instance_p, FCanConfig *config_p)
{
    FT_ASSERTNONVOID(instance_p != NULL);
    FT_ASSERTNONVOID(config_p != NULL);
    instance_p->config = *config_p;
    instance_p->is_ready = FT_COMPONENT_IS_READY;
    instance_p->use_canfd = FT_CAN_USE_CANFD;
    FCanReset(instance_p);

    return FCAN_SUCCESS;
}

void FCanGetErrorCnt(FCan *instance_p, u32 *txerr, u32 *rxerr)
{
    FCanConfig *config_p;
    FT_ASSERTVOID(instance_p != NULL);
    FT_ASSERTVOID(instance_p->is_ready == FT_COMPONENT_IS_READY);
    config_p = &instance_p->config;

    *rxerr = FCAN_READREG(config_p->base_address, FCAN_ERR_CNT_OFFSET) & FCAN_ERR_CNT_RFN_MASK;
    *txerr = (FCAN_READREG(config_p->base_address, FCAN_ERR_CNT_OFFSET) & FCAN_ERR_CNT_TFN_MASK) >> FCAN_ERR_CNT_TFN_SHIFT;
}

u32 FCanRecv(FCan *instance_p, struct FCanFrame *frame_p)
{
    FCanConfig *config_p;
    u32 canid;
    u32 dlc;

    int i = 0, j = 0;
    FT_ASSERTZERONUM(instance_p != NULL);
    FT_ASSERTZERONUM(instance_p->is_ready == FT_COMPONENT_IS_READY);
    FT_ASSERTZERONUM(frame_p != NULL);
    config_p = &instance_p->config;

    /* Read a frame from Phytium CAN */
    canid = FCAN_READREG(config_p->base_address, FCAN_RX_FIFO_OFFSET);
    /* if canid is big-endian ,use swap change to little-endian */
    canid = be32_to_cpu(canid);

    /* identifier extension */
    if (canid & FCAN_IDR_IDE_MASK)
    {
        dlc = FCAN_READREG(config_p->base_address, FCAN_RX_FIFO_OFFSET);
        dlc = be32_to_cpu(dlc);

        if (dlc & FTCANFD_ID2_FDL_MASK)
        {
            if (dlc & FTCANFD_ID2_BRS_MASK)
            {
                frame_p->flags |= CANFD_BRS;
            }

            if (dlc & FTCANFD_ID2_ESI_MASK)
            {
                frame_p->flags |= CANFD_ESI;
            }
            dlc = ((dlc & FTCANFD_ID2_EDLC_MASK) >> FCANFD_IDR_GET_EDLC_SHIFT);
        }
        else
        {
            dlc = ((dlc & FCAN_IDR_EDLC_MASK) >> FCAN_IDR_EDLC_SHIFT);
        }

        frame_p->canid = (canid & FCAN_IDR_ID1_MASK) >> 3;
        frame_p->canid |= (canid & FCAN_IDR_ID2_MASK) >> FCAN_IDR_ID2_SHIFT;
        frame_p->canid |= CAN_EFF_FLAG;

        if (canid & FCAN_IDR_RTR_MASK)
        {
            frame_p->canid |= CAN_RTR_FLAG;
        }
    }
    else
    {

        if (canid & FTCANFD_ID1_FDL_MASK)
        {
            if (canid & FTCANFD_ID1_BRS_MASK)
            {
                frame_p->flags |= CANFD_BRS;
            }

            if (canid & FTCANFD_ID1_ESI_MASK)
            {
                frame_p->flags |= CANFD_ESI;
            }
            dlc = ((canid & FTCANFD_ID1_SDLC_MASK) >> FCANFD_IDR1_SDLC_SHIFT);
        }
        else
        {
            dlc = ((canid & FCAN_IDR_DLC_MASK) >> FCAN_IDR_SDLC_SHIFT);
        }

        /* The received frame is a standard format frame */
        frame_p->canid = (canid & FCAN_IDR_ID1_MASK) >> FCAN_IDR_ID1_SHIFT;
        if (canid & FCAN_IDR_SRR_MASK)
        {
            frame_p->canid |= CAN_RTR_FLAG;
        }
    }

    frame_p->candlc = (dlc > sizeof(frame_p->data)) ? sizeof(frame_p->data) : dlc;

    if (!(frame_p->canid & CAN_RTR_FLAG))
    {
        j = 0;
        for (i = frame_p->candlc; i > 0; i -= 4)
        {
            *(u32 *)(frame_p->data + j) = FCAN_READREG(config_p->base_address, FCAN_RX_FIFO_OFFSET);
            j += 4;
        }

        if (i > 0)
        {

            *(u32 *)(frame_p->data + j) = FCAN_READREG(config_p->base_address, FCAN_RX_FIFO_OFFSET);
        }
    }

    return FCAN_SUCCESS;
}

u32 FCanSend(FCan *instance_p, struct FCanFrame *frame_p)
{
    u32 id, dlc;
    FCanConfig *config_p;
    int i = 0, j = 0;
    FT_ASSERTNONVOID(instance_p != NULL);
    FT_ASSERTNONVOID(instance_p->is_ready == FT_COMPONENT_IS_READY);
    config_p = &instance_p->config;

    if (frame_p->canid & CAN_EFF_FLAG)
    {

        /* Extended CAN id format */
        id = ((frame_p->canid & CAN_EFF_MASK) << FCAN_IDR_ID2_SHIFT) &
             FCAN_IDR_ID2_MASK;
        id |= (((frame_p->canid & CAN_EFF_MASK) >>
                (CAN_EFF_ID_BITS - CAN_SFF_ID_BITS))
               << FCAN_IDR_ID1_SHIFT) &
              FCAN_IDR_ID1_MASK;

        /* The substibute remote TX request bit should be "1"
		 * for extended frames as in the Phytium CAN datasheet
		 */
        id |= FCAN_IDR_IDE_MASK | FCAN_IDR_SRR_MASK;
        if (frame_p->canid & CAN_RTR_FLAG)
        {
            id |= FCAN_IDR_RTR_MASK;
        }

        if (instance_p->use_canfd)
        {
            dlc = (u32)frame_p->candlc << FCANFD_IDR_EDLC_SHIFT;
            dlc |= FTCANFD_ID2_FDL_MASK;
            if (frame_p->flags & CANFD_BRS)
            {
                dlc |= FTCANFD_ID2_BRS_MASK;
            }

            if (frame_p->flags & CANFD_ESI)
            {
                dlc |= FTCANFD_ID2_ESI_MASK;
            }
        }
        else
        {
            dlc = (u32)frame_p->candlc << FCAN_IDR_EDLC_SHIFT;
        }

        FCAN_WRITEREG(config_p->base_address, FCAN_TX_FIFO_OFFSET, be32_to_cpu(id));
        FCAN_WRITEREG(config_p->base_address, FCAN_TX_FIFO_OFFSET, be32_to_cpu(dlc));
    }
    else
    {
        /* Standard CAN id format */
        id = ((frame_p->canid & CAN_SFF_MASK) << FCAN_IDR_ID1_SHIFT) &
             FCAN_IDR_ID1_MASK;

        if (frame_p->canid & CAN_RTR_FLAG)
            id |= FCAN_IDR_SRR_MASK;

        if (instance_p->use_canfd)
        {
            dlc = ((frame_p->candlc << FCANFD_IDR_EDLC_SHIFT) | FTCANFD_IDR_ID1_MASK);

            dlc |= FTCANFD_ID1_FDL_MASK;

            if (frame_p->flags & CANFD_BRS)
            {
                dlc |= FTCANFD_ID1_BRS_MASK;
            }

            if (frame_p->flags & CANFD_ESI)
            {
                dlc |= FTCANFD_ID1_ESI_MASK;
            }
        }
        else
        {
            dlc = ((frame_p->candlc << FCAN_IDR_SDLC_SHIFT) | FCAN_IDR_PAD_MASK);
        }

        id |= dlc;
        FCAN_WRITEREG(config_p->base_address, FCAN_TX_FIFO_OFFSET, be32_to_cpu(id));
    }

    if (!(frame_p->canid & CAN_RTR_FLAG))
    {
        j = 0;
        for (i = frame_p->candlc; i > 0; i -= 4)
        {
            FCAN_WRITEREG(config_p->base_address, FCAN_TX_FIFO_OFFSET, *(u32 *)(frame_p->data + j));
            j += 4;
        }

        if (i > 0)
        {
            FCAN_WRITEREG(config_p->base_address, FCAN_TX_FIFO_OFFSET, *(u32 *)(frame_p->data + j));
        }
    }

    /* triggers tranmission */
    FCAN_CLEARBIT(config_p->base_address, FCAN_CTRL_OFFSET, FCAN_CTRL_XFER_MASK);
    FCAN_SETBIT(config_p->base_address, FCAN_CTRL_OFFSET, FCAN_CTRL_TXREQ_MASK | FCAN_CTRL_XFER_MASK);

    return FCAN_SUCCESS;
}

ft_error_t FCanSetTiming(FCan *instance_p,
                         struct FCanBittiming *bittiming_arb_p,
                         struct FCanBittiming *bittiming_data_p)
{
    u32 btr = 0, dbtr = 0;
    FCanConfig *config_p;
    u32 IsConfigMode;
    FT_ASSERTNONVOID(instance_p != NULL);
    FT_ASSERTNONVOID(instance_p->is_ready == FT_COMPONENT_IS_READY);
    config_p = &instance_p->config;

    FT_ASSERTNONVOID(bittiming_arb_p->brp != 0);
    FT_ASSERTNONVOID(bittiming_arb_p->prop_seg != 0);
    FT_ASSERTNONVOID(bittiming_arb_p->phase_seg1 != 0);
    FT_ASSERTNONVOID(bittiming_arb_p->phase_seg2 != 0);

    /* Setting Baud Rate prescalar value in BRPR Register */
    btr = (bittiming_arb_p->brp - 1) << 16;
    btr |= (bittiming_arb_p->prop_seg - 1) << 2;
    btr |= (bittiming_arb_p->phase_seg1 - 1) << 5;
    btr |= (bittiming_arb_p->phase_seg2 - 1) << 8;
    btr |= (bittiming_arb_p->sjw - 1);

    dbtr = (bittiming_data_p->brp - 1) << 16;
    dbtr |= (bittiming_data_p->prop_seg - 1) << 2;
    dbtr |= (bittiming_data_p->phase_seg1 - 1) << 5;
    dbtr |= (bittiming_data_p->phase_seg2 - 1) << 8;
    dbtr |= (bittiming_data_p->sjw - 1);

    IsConfigMode = (FCAN_READREG(config_p->base_address, FCAN_CTRL_OFFSET) & FCAN_CTRL_XFER_MASK);

    if (IsConfigMode)
    {
        FCAN_CLEARBIT(config_p->base_address, FCAN_CTRL_OFFSET, FCAN_CTRL_XFER_MASK);
    }

    if (btr && dbtr)
    {
        FCAN_WRITEREG(config_p->base_address, FCAN_DAT_RATE_CTRL_OFFSET, dbtr);
        FCAN_WRITEREG(config_p->base_address, FCAN_ARB_RATE_CTRL_OFFSET, btr);
    }
    else
    {

        FCAN_WRITEREG(config_p->base_address, FCAN_DAT_RATE_CTRL_OFFSET, btr);
        FCAN_WRITEREG(config_p->base_address, FCAN_ARB_RATE_CTRL_OFFSET, btr);
    }

    /*Enable Transfer*/
    FCAN_SETBIT(config_p->base_address, FCAN_CTRL_OFFSET, FCAN_CTRL_XFER_MASK);

    return FCAN_SUCCESS;
}

void FCanEnable(FCan *instance_p)
{
    FCanConfig *config_p;
    FT_ASSERTVOID(instance_p != NULL);
    FT_ASSERTVOID(instance_p->is_ready == FT_COMPONENT_IS_READY);
    config_p = &instance_p->config;

    FCAN_SETBIT(config_p->base_address, FCAN_CTRL_OFFSET, FCAN_CTRL_XFER_MASK);
}

void FCanDisable(FCan *instance_p)
{
    FCanConfig *config_p;
    FT_ASSERTVOID(instance_p != NULL);
    FT_ASSERTVOID(instance_p->is_ready == FT_COMPONENT_IS_READY);
    config_p = &instance_p->config;

    FCAN_CLEARBIT(config_p->base_address, FCAN_CTRL_OFFSET, FCAN_CTRL_XFER_MASK | FCAN_CTRL_TXREQ_MASK | FCAN_CTRL_AIME_MASK);
    FCAN_WRITEREG(config_p->base_address, FCAN_INTR_OFFSET, FCAN_INTR_BOIC_MASK | FCAN_INTR_PWIC_MASK | FCAN_INTR_PEIC_MASK | FCAN_INTR_RFIC_MASK | FCAN_INTR_TFIC_MASK | FCAN_INTR_REIC_MASK | FCAN_INTR_TEIC_MASK | FCAN_INTR_EIC_MASK);
}

ft_error_t FCanIdMaskFilterSet(FCan *instance_p, u32 filter_index, u32 frame_type, u32 id, u32 mask)
{
    FCanConfig *config_p;
    u32 id_reg_offset, mask_reg_offset;
    FT_ASSERTNONVOID(instance_p != NULL);
    FT_ASSERTNONVOID(instance_p->is_ready == FT_COMPONENT_IS_READY);
    FT_ASSERTNONVOID(filter_index <= 4);
    config_p = &instance_p->config;

    FCAN_WRITEREG(config_p->base_address, FCAN_CTRL_OFFSET, FCAN_CTRL_RST_MASK);

    if (FCAN_STANDARD_FRAME == frame_type)
    {
        id = id << FCAN_ACC_IDN_SHIFT;
        mask = mask << FCAN_ACC_IDN_SHIFT;
    }

    switch (filter_index)
    {
    case 0:
        id_reg_offset = FCAN_ACC_ID0_OFFSET;
        mask_reg_offset = FCAN_ACC_ID0_MASK_OFFSET;
        break;
    case 1:
        id_reg_offset = FCAN_ACC_ID1_OFFSET;
        mask_reg_offset = FCAN_ACC_ID1_MASK_OFFSET;
        break;
    case 2:
        id_reg_offset = FCAN_ACC_ID2_OFFSET;
        mask_reg_offset = FCAN_ACC_ID2_MASK_OFFSET;
        break;
    case 3:
        id_reg_offset = FCAN_ACC_ID3_OFFSET;
        mask_reg_offset = FCAN_ACC_ID3_MASK_OFFSET;
        break;
    default:
        return FCAN_FAILURE;
    }

    FCAN_WRITEREG(config_p->base_address, id_reg_offset, id);
    FCAN_WRITEREG(config_p->base_address, mask_reg_offset, mask);

    FCAN_CLEARBIT(config_p->base_address, FCAN_CTRL_OFFSET, FCAN_CTRL_RST_MASK);
    FCAN_SETBIT(config_p->base_address, FCAN_CTRL_OFFSET, FCAN_CTRL_XFER_MASK | FCAN_CTRL_AIME_MASK);
    return FCAN_SUCCESS;
}

void FCanIdMaskFilterEnable(FCan *instance_p)
{
    FCanConfig *config_p;
    FT_ASSERTVOID(instance_p != NULL);
    FT_ASSERTVOID(instance_p->is_ready == FT_COMPONENT_IS_READY);
    config_p = &instance_p->config;
    FCAN_SETBIT(config_p->base_address, FCAN_CTRL_OFFSET, FCAN_CTRL_AIME_MASK);
}

void FCanIdMaskFilterDisable(FCan *instance_p)
{
    FCanConfig *config_p;
    FT_ASSERTVOID(instance_p != NULL);
    FT_ASSERTVOID(instance_p->is_ready == FT_COMPONENT_IS_READY);
    config_p = &instance_p->config;
    FCAN_CLEARBIT(config_p->base_address, FCAN_CTRL_OFFSET, FCAN_CTRL_AIME_MASK);
}